using System;
using NUnit.Framework;
using OpenRay;
using System.Collections.Generic;

using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Security.Cryptography;
using System.Diagnostics;

namespace Tests
{
	[TestFixture]
	public class TestTriangle
	{
		private void SavePNG( FrameBuffer fb, string s ) {
			Bitmap bmp = new Bitmap(fb.Width, fb.Height );
			
			for ( int y = 0; y < fb.Height; y++ )
			{
				for ( int x = 0; x < fb.Width; x++ )
				{
					float[] color = fb.GetArgb(x,y);
					bmp.SetPixel(x, y, System.Drawing.Color.FromArgb( (int)(color[0] * 255f), (int)(color[1] * 255f), (int)(color[2] * 255f), (int)(color[3] * 255f) ) );
				}
			}
			
			bmp.Save(s, ImageFormat.Png );
			bmp.Dispose();
		}
		
		private string ComputeMD5( FrameBuffer fb ) {
			int length = fb.Width * fb.Height * 4 * 4;
			
			byte[] bytes = new byte[length];
			
			for( int i = 0, x = 0; i < fb.Height; i++ ) 
			{
				for ( int j = 0; j < fb.Width; j++ )
				{
					float[] pixel = fb.GetArgb(j, i);
					Debug.Assert( pixel.Length == 4 );
					foreach ( float f in pixel )
					{
						byte[] t = BitConverter.GetBytes(f);
						Debug.Assert( t.Length == 4 );
						foreach ( byte b in t )
						{
							bytes[x++] = b;
						}
					}
				}
			}
			
			MD5 md5 = new MD5CryptoServiceProvider();
			byte[] retVal = md5.ComputeHash(bytes);
			
			StringBuilder sb = new StringBuilder();
			
			for ( int i = 0; i < retVal.Length; i++ )
			{
				sb.Append(retVal[i].ToString("x2"));
			}
			
			return sb.ToString();
		}
		
		[Test]
		public void TestTriangleFlat ()
		{
			IScene s = new ArrayScene();
			
			OpenRay.Point[] p = new OpenRay.Point[3];
			p[0] = new OpenRay.Point( 0f, 0f, 0f, 1f );
			p[1] = new OpenRay.Point( 0f, 1f, 0f, 1f );
			p[2] = new OpenRay.Point( 1f, 0f, 0f, 1f );
			
			Material[] m = new Material[1] { new ColorMaterial( new OpenRay.Color( 1f, 0f, 0f, 1f ) ) };
			
			Vector[] n = new Vector[1] { new Vector( 0f, 0f, -1f ) };
			
			FlatTriangleGeometry geom = new FlatTriangleGeometry( p, n, m );
			Primitive prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, new List<OpenRay.Light>() );
			
			s.AddPrimitive( prim );
			
			OpenGLCamera camera = new OpenGLCamera(s, Matrix.Identity, 500, 500 );
			
			FrameBuffer fb = camera.Render();
			string md5 = ComputeMD5( fb );
			string result = "9642531b2333b9b0464dd9e928326967";
			
			if ( !md5.Equals(result) )
			{
				SavePNG(fb, "TestTriangleFlat.png" );
				Console.WriteLine(md5);
				Assert.AreEqual(result, s);
			}
		}
		
		[Test]
		public void TestTriangleSmoothOneColor ()
		{
			IScene s = new ArrayScene();
			
			OpenRay.Point[] p = new OpenRay.Point[3];
			p[0] = new OpenRay.Point( 0f, 0f, 0f, 1f );
			p[1] = new OpenRay.Point( 0f, 1f, 0f, 1f );
			p[2] = new OpenRay.Point( 1f, 0f, 0f, 1f );
			
			Material[] m = new Material[3];
			m[0] = new ColorMaterial( new OpenRay.Color( 1f, 0f, 0f, 1f ) );
			m[1] = new ColorMaterial( new OpenRay.Color( 1f, 0f, 0f, 1f ) );
			m[2] = new ColorMaterial( new OpenRay.Color( 1f, 0f, 0f, 1f ) );
			
			Vector[] n = new Vector[3];
			n[0] = new Vector( 0f, 0f, -1f );
			n[1] = new Vector( 0f, 0f, -1f );
			n[2] = new Vector( 0f, 0f, -1f );
			
			SmoothTriangleGeometry geom = new SmoothTriangleGeometry( p, n, m );
			Primitive prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, new List<OpenRay.Light>() );
			
			s.AddPrimitive( prim );
			
			OpenGLCamera camera = new OpenGLCamera(s, Matrix.Identity, 500, 500 );
			
			FrameBuffer fb = camera.Render();
			string md5 = ComputeMD5( fb );
			string result = "9642531b2333b9b0464dd9e928326967";
			
			if ( !md5.Equals(result) )
			{
				SavePNG(fb, "TestTriangleSmoothOneColor.png");
				Console.WriteLine(md5);
				Assert.AreEqual(result, s);
			}
		}
		
		[Test]
		public void TestTriangleSmoothThreeColor ()
		{
			IScene s = new ArrayScene();
			
			OpenRay.Point[] p = new OpenRay.Point[3];
			p[0] = new OpenRay.Point( 0f, 0f, 0f, 1f );
			p[1] = new OpenRay.Point( 0f, 1f, 0f, 1f );
			p[2] = new OpenRay.Point( 1f, 0f, 0f, 1f );
			
			Material[] m = new Material[3];
			m[0] = new ColorMaterial( new OpenRay.Color( 1f, 0f, 0f, 1f ) );
			m[1] = new ColorMaterial( new OpenRay.Color( 0f, 1f, 0f, 1f ) );
			m[2] = new ColorMaterial( new OpenRay.Color( 0f, 0f, 1f, 1f ) );
			
			Vector[] n = new Vector[3];
			n[0] = new Vector( 0f, 0f, -1f );
			n[1] = new Vector( 0f, 0f, -1f );
			n[2] = new Vector( 0f, 0f, -1f );
			
			SmoothTriangleGeometry geom = new SmoothTriangleGeometry( p, n, m );
			Primitive prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, new List<OpenRay.Light>() );
			
			s.AddPrimitive( prim );
			
			OpenGLCamera camera = new OpenGLCamera(s, Matrix.Identity, 500, 500 );
			
			FrameBuffer fb = camera.Render();
			string md5 = ComputeMD5( fb );
			string result = "8d1a44a068d71e596e48d1310eb07530";
			
			if ( !md5.Equals(result) )
			{
				SavePNG(fb, "TestTriangleSmoothThreeColor.png" );
				Console.WriteLine(md5);
				Assert.AreEqual(result, s);
			}
		}
	}
}

